#ifndef _GAME_PACKETS_H_
#define _GAME_PACKETS_H_

#include <cstring>
#include <string>

#include "../commands/GameCommand.h"
#include "../../tools/Log.h"
#include "../../tools/network/UDPPacket.h"

namespace GamePacket
{

const unsigned int MAX_PACKET_LEN = 1500;	// (Size of an Ethernet packet - anything larger risks fragmentation)
const unsigned int MAX_COMMANDS = 25;		// Max # of GameCommands per update before sizeof(Update) exceeds MAX_PACKET_LEN
const unsigned int PACKET_TIMEOUT = 500;//175;	// Time (in ms) before we assume a packet was dropped

enum PacketType { ACK, UPDATE };

/** Note: Do not introduce a vtable into any of the structs in this file. They need to be dumb data buffers that we can
	recvfrom into directly.
*/
struct Header
{
	unsigned short type;
	unsigned short packet_len;
	unsigned long seq_num;
	
	Header(): type(0), packet_len(0), seq_num(0) { }
	Header(void *packet);
	Header(unsigned int Type, unsigned short Packet_len, unsigned long Seq_num) :
		type(Type), packet_len(Packet_len), seq_num(Seq_num) { }
	
	void hton();
	void ntoh();
	void print();
};

class Ack : UDPPacket
{
	struct Data
	{
		Header header; 
		
		Data(): header() {}
	};
	
	void hton();
	void ntoh();

public:
	Data data;
	
	Ack() { };
	Ack(void *packet);
	Ack(unsigned long seq_num);
	
	void convert_and_send(int sock, struct sockaddr_in *to);
};

class Update : public UDPPacket
{
	// If this struct changes, make sure add_command is still correct (updates header.packet_len)
	struct Data
	{
		Header header;
		
		unsigned long ok_time;			// The game time up to which this update includes commands for (strictly less than)
		unsigned long num_commands;		// Number of commands this update contains
		
		SerializedGameCommand commands[MAX_COMMANDS];
		
		Data(): header(), ok_time(0), num_commands(0) {}
	};
	
	void hton();
	void ntoh();
	int get_current_size();

public:
	Data data;
	
	Update(): data() { }
	
	Update(void *packet);
	Update(unsigned long ok_time, unsigned int seq_num);
	
	void add_command(GameCommand& cmd);	// Updates header.packet_len
	void convert_and_send(int sock, struct sockaddr_in *to);
};

}
#endif
